﻿#region 文档操作记录
// DeviceCenter.cs
// 说明：
// 
// 创建日期：11:11
// 文档作者：饶杰
//  
// 更新日志：
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using PullSdkHelper.Tables;

namespace PullSdkHelper
{
    /// <summary>
    /// 门禁设备中心
    /// </summary>
    public class DeviceCenter : IDisposable
    {
        private static Random _random = new Random();
        private string ConnectionString { get; }
        private int _tryCount = 0;
        private IntPtr _connectionHandle = IntPtr.Zero;

        //public 
        private DeviceCenter(string connectionString)
        {
            this.ConnectionString = connectionString;

            Connect();
        }

        #region init

        /// <summary>
        /// 事件类型
        /// </summary>
        private static readonly Dictionary<short, string> EventTypeTexts = new Dictionary<short, string>
        {
            { 0 ,"正常刷卡开门" },
            { 1 ,"常开时间段内刷卡" },
            { 2 ,"首卡开门(刷卡)" },
            { 3 ,"多卡开门(刷卡)" },
            { 4 ,"紧急状态密码开门" },
            { 5 ,"常开时间段开门" },
            { 6 ,"触发联动事件" },
            { 7 ,"取消报警" },
            { 8 ,"远程开门" },
            { 9 ,"远程关门" },
            { 10 ,"禁用当天常开时段" },
            { 11 ,"启用当天常开时段" },
            { 12 ,"开启辅助输出" },
            { 13 ,"关闭辅助输出" },
            { 14 ,"正常按指纹开门" },
            { 15 ,"多卡开门(按指纹)" },
            { 16 ,"常开时间段内按指纹" },
            { 17 ,"卡加指纹开门" },
            { 18 ,"首卡开门(按指纹)" },
            { 19 ,"首卡开门(卡加指纹)" },
            { 20 ,"刷卡间隔太短" },
            { 21 ,"门非有效时间段(刷卡)" },
            { 22 ,"非法时间段" },
            { 23 ,"非法访问" },
            { 24 ,"反潜" },
            { 25 ,"互锁" },
            { 26 ,"多卡验证(刷卡)" },
            { 27 ,"卡未注册" },
            { 28 ,"门开超时" },
            { 29 ,"卡已过有效期" },
            { 30 ,"密码错误" },
            { 31 ,"按指纹间隔太短" },
            { 32 ,"多卡验证(按指纹)" },
            { 33 ,"指纹已过有效期" },
            { 34 ,"指纹未注册" },
            { 35 ,"门非有效时间段(按指纹)" },
            { 36 ,"门非有效时间段(按出门按钮)" },
            { 37 ,"常开时间段无法关门" },
            { 38 ,"卡已挂失" },
            { 39 ,"黑名单" },
            { 40 ,"多指纹验证失败" },
            { 41 ,"验证方式错误" },
            { 42 ,"韦根格式错误" },
            { 43 ,"后台验证" },
            { 44 ,"后台验证失败" },
            { 45 ,"后台验证超时" },
            { 46 ,"后台验证事件" },
            { 47 ,"发送命令失败" },
            { 48 ,"多卡开门失败" },
            { 100 ,"防拆报警" },
            { 101 ,"胁迫密码开门" },
            { 102 ,"门被意外打开" },
            { 103 ,"胁迫指纹开门" },
            { 200 ,"门已打开" },
            { 201 ,"门已关闭" },
            { 202 ,"出门按钮开门" },
            { 203 ,"多卡开门(卡加指纹)" },
            { 204 ,"常开时间段结束" },
            { 205 ,"远程开门常开" },
            { 206 ,"设备启动" },
            { 207 ,"密码开门" },
            { 208 ,"超级用户开门" },
            { 209 ,"门被锁定" },
            { 210 ,"消防开启" },
            { 211 ,"超级用户关门" },
            { 220 ,"辅助输入点断开" },
            { 221 ,"辅助输入点短路" },
            { 222 ,"后台验证成功" },
            { 223 ,"后台验证网络不稳开门" },
            { 224 ,"后台重新启用反潜" },
            { 255 ,"门/报警状态" },

        };
        /// <summary>
        /// 获取门禁记录的类型
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public static string TryGetEventType(short code)
        {
            if (EventTypeTexts.ContainsKey(code))
                return EventTypeTexts[code];
            return "其他/未知";
        }

        private static readonly Dictionary<int, string> ErrorCodes = new Dictionary<int, string>
        {
            {-1, "命令发送失败"},
            {-2, "命令没有回应"},
            {-3, "需要的缓存不足 "},
            {-4, "解压失败 "},
            {-5, "读取数据长度不对 "},
            {-6, "解压的长度和期望的长度不一致 "},
            {-7, "命令重复 "},
            {-8, "连接尚未授权 "},
            {-9, "数据错误，CRC 校验失败 "},
            {-10, "数据错误，PullSDK 无法解析 "},
            {-11, "数据参数错误 "},
            {-12, "命令执行错误 "},
            {-13, "命令错误，没有此命令 "},
            {-14, "通讯密码错误 "},
            {-15, "写文件失败 "},
            {-16, "读文件失败 "},
            {-17, "文件不存在 "},
            {-18, "设备空间不足 "},
            {-19, "校验和出错 "},
            {-20, "接受到的数据长度与给出的数据长度不一致 "},
            {-21, "设备中，没有设置平台参数 "},
            {-22, "固件升级，传来的固件的平台与本地的平台不一致 "},
            {-23, "升级的固件版本比设备中的固件版本老 "},
            {-24, "升级的文件标识出错 "},
            {-25, "固件升级，传来的文件名不对，即不是 emfw.cfg "},
            {-26, "传来的指纹模板长度为 0 "},
            {-27, "传来的指纹 pin 号错误，找不到此用户 "},
            {-28, "常开时间段执行开门命令 "},
            {-98, "区域错误，禁止连接 "},
            {-99, "未知错误 "},
            {-100, "表结构不存在 "},
            {-101, "表结构中，条件字段不存在 "},
            {-102, "字段总数不一致 "},
            {-103, "字段排序不一致 "},
            {-104, "实时事件数据错误 "},
            {-105, "解析数据时，数据错误 "},
            {-106, "数据溢出，下发数据超出 4M "},
            {-107, "获取表结构失败 "},
            {-108, "无效 OPTIONS 选项 "},
            {-112, "PC 传入的数据接收缓冲不足 "},
            {-201, "LoadLibrary 失败 "},
            {-202, "调用接口失败 "},
            {-203, "通讯初始化失败 "},
            {-206, "串口代理程序启动失败，原因一般是串口不存在或串口被占用 "},
            {-301, "获取 TCP/IP 版本失败 "},
            {-302, "错误版本号 "},
            {-303, "获取协议类型失败 "},
            {-304, "无效 SOCKET "},
            {-305, "SOCKET 错误 "},
            {-306, "HOST 错误 "},
            {-307, "连接超时 "},
            {10035, "资源暂时不可用 "},
            {10038, "一个操作已尝试在不套接字的。  "},
            {10054, "通过对等方重置的连接。  "},
            {10060, "连接超时 "},
            {10061, "连接被拒绝 "},
            {10065, "无路由主机"},
        };

        public static string TryGetError(short code)
        {
            if (ErrorCodes.ContainsKey(code))
                return ErrorCodes[code];
            return "其他/未知";
        }

        public bool IsConnected
        {
            get { return _connectionHandle != IntPtr.Zero; }
        }

        private void Connect()
        {
            DisConnect();
            _connectionHandle = ZKSdk.Connect(this.ConnectionString);
            if (_connectionHandle == new IntPtr(-307))
            {
                if (_tryCount < 3) //连接超时重新连接3次
                {
                    _tryCount++;
                    Connect();
                }
            }
            else
            {
                _tryCount = 0;
            }
        }

        private void DisConnect()
        {
            if (IsConnected)
            {
                ZKSdk.Disconnect(_connectionHandle);
                _connectionHandle = IntPtr.Zero;
            }
        }

        public static DeviceCenter TcpConnect(string ipaddress, int port=4370, string password = "", int timeout = 2000)
        {
            return
                new DeviceCenter(String.Format("protocol=TCP,ipaddress={0},port={1},timeout={2},passwd={3}", ipaddress,
                    port, timeout, password));
        }

        public static DeviceCenter Rs485Connect(string port, string baudrate, int deviceid, string password = "",
            int timeout = 2000)
        {
            return
                new DeviceCenter(
                    String.Format("protocol=RS485,port={0},baudrate={1},deviceid={2},timeout={3},passwd={4}", port,
                        baudrate, deviceid, timeout, password));
        }

        #endregion

        /// <summary>
        /// 开门
        /// </summary>
        /// <param name="doorNum">门编号</param>
        /// <param name="openSecond">开门持续时长,开门取值范围1~60, 255表示常开</param>
        /// <returns>返回为 0 或者正数时，表示成功；返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 </returns>
        public int OpenDoor(int doorNum,short openSecond = 15)
        {
            if (openSecond <= 0) openSecond = 1;
            if (openSecond != 255)
            {
                if (openSecond > 60) openSecond = 60;
            }
            var ret = ZKSdk.ControlDevice(_connectionHandle, 1, doorNum, 1, openSecond, 0, "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int) longId);
        }


        /// <summary>
        /// 关门
        /// </summary>
        /// <param name="doorNum">门编号</param>
        /// <returns>
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int ColseDoor(int doorNum)
        {
            var ret = ZKSdk.ControlDevice(_connectionHandle, 1, doorNum, 1, 0, 0, "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }
        

        /// <summary>
        /// 禁用常开
        /// </summary>
        /// <param name="doorNum">门编号</param>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int DisableAlwaysOpen(int doorNum)
        {
            var ret = ZKSdk.ControlDevice(_connectionHandle, 4, doorNum, 0, 0, 0, "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }


        /// <summary>
        /// 启用常开
        /// </summary>
        /// <param name="doorNum">门编号</param>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int EnableAlwaysOpen(int doorNum)
        {
            var ret = ZKSdk.ControlDevice(_connectionHandle, 4, doorNum, 1, 0, 0, "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }
        /// <summary>
        /// 取消预警
        /// </summary>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int CancelAlarm()
        {
            var ret = ZKSdk.ControlDevice(_connectionHandle, 2, 0, 0, 0, 0, "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }

        /// <summary>
        /// 更新用户信息,如果主键已经在设备中,则覆盖原记录
        /// </summary>
        /// <param name="user">需要更新的用户信息,信息必须完整</param>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int SetUserInfo(TblUser user)
        {
            if (user == null
                || user.Pin <= 0)
            {
                return -11; //数据参数错误
            }
            var tableTable = "user";
            StringBuilder data = new StringBuilder();
            data.Append("Pin="+ user.Pin);
            data.Append("\tCardNo=" + user.CardNo);
            data.Append("\tPassword=" + user.Password);
            data.AppendFormat("\tStartTime={0:yyyyMMdd}",user.StartTime);
            data.AppendFormat("\tEndTime={0:yyyyMMdd}" , user.EndTime);
            data.Append("\tGroup=" + user.Group);
            data.Append("\tName=" + user.Name);
            data.Append("\tSuperAuthorize=" + user.SuperAuthorize);
            var ret = ZKSdk.SetDeviceData(_connectionHandle, tableTable, data.ToString(), "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }

        /// <summary>
        /// 设置时间段
        /// </summary>
        /// <param name="timezone"></param>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int SetTimezone(TblTimezone timezone)
        {
            if (timezone == null
                || timezone.TimezoneId <= 0)
            {
                return -11; //数据参数错误
            }
            var tableTable = "timezone";
            StringBuilder data = new StringBuilder(512);
            data.Append("TimezoneId=" + timezone.TimezoneId);
            data.Append("\tSunTime1=" + timezone.SunTime1.TimeValue);
            data.Append("\tSunTime2=" + timezone.SunTime2.TimeValue);
            data.Append("\tSunTime3=" + timezone.SunTime3.TimeValue);
            data.Append("\tMonTime1=" + timezone.MonTime1.TimeValue);
            data.Append("\tMonTime2=" + timezone.MonTime2.TimeValue);
            data.Append("\tMonTime3=" + timezone.MonTime3.TimeValue);
            data.Append("\tTueTime1=" + timezone.TueTime1.TimeValue);
            data.Append("\tTueTime2=" + timezone.TueTime2.TimeValue);
            data.Append("\tTueTime3=" + timezone.TueTime3.TimeValue);

            data.Append("\tWedTime1=" + timezone.WedTime1.TimeValue);
            data.Append("\tWedTime2=" + timezone.WedTime2.TimeValue);
            data.Append("\tWedTime3=" + timezone.WedTime3.TimeValue);
            data.Append("\tThuTime1=" + timezone.ThuTime1.TimeValue);
            data.Append("\tThuTime2=" + timezone.ThuTime2.TimeValue);
            data.Append("\tThuTime3=" + timezone.ThuTime3.TimeValue);
            data.Append("\tFriTime1=" + timezone.FriTime1.TimeValue);
            data.Append("\tFriTime2=" + timezone.FriTime2.TimeValue);
            data.Append("\tFriTime3=" + timezone.FriTime3.TimeValue);
            data.Append("\tSatTime1=" + timezone.SatTime1.TimeValue);
            data.Append("\tSatTime2=" + timezone.SatTime2.TimeValue);
            data.Append("\tSatTime3=" + timezone.SatTime3.TimeValue);

            data.Append("\tHol1Time1=" + timezone.Hol1Time1.TimeValue);
            data.Append("\tHol1Time2=" + timezone.Hol1Time2.TimeValue);
            data.Append("\tHol1Time3=" + timezone.Hol1Time3.TimeValue);
            data.Append("\tHol2Time1=" + timezone.Hol2Time1.TimeValue);
            data.Append("\tHol2Time2=" + timezone.Hol2Time2.TimeValue);
            data.Append("\tHol2Time3=" + timezone.Hol2Time3.TimeValue);
            data.Append("\tHol3Time1=" + timezone.Hol3Time1.TimeValue);
            data.Append("\tHol3Time2=" + timezone.Hol3Time2.TimeValue);
            data.Append("\tHol3Time3=" + timezone.Hol3Time3.TimeValue);

            var ret = ZKSdk.SetDeviceData(_connectionHandle, tableTable, data.ToString(), "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }

        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="pin"></param>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int DelUserInfo(int pin)
        {
            if (pin <= 0)
            {
                return -11; //数据参数错误
            }
            var tableTable = "user";
            StringBuilder data = new StringBuilder(13);
            data.Append("Pin=" + pin);
            var ret = ZKSdk.DeleteDeviceData(_connectionHandle, tableTable, data.ToString(), "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }
        /// <summary>
        /// 删除时间段配置
        /// </summary>
        /// <param name="timezoneId"></param>
        /// <returns> 
        /// 返回为 0 或者正数时，表示成功；
        /// 返回负数时表示失败 调用 DeviceCenter.TryGetError(ret) 方法获取错误描述 
        /// </returns>
        public int DelTimezone(int timezoneId)
        {
            if (timezoneId <= 0)
            {
                return -11; //数据参数错误
            }
            var tableTable = "timezone";
            StringBuilder data = new StringBuilder(20);
            data.Append("TimezoneId=" + timezoneId);
            var ret = ZKSdk.DeleteDeviceData(_connectionHandle, tableTable, data.ToString(), "");
            var longId = ret.GetValue();
            if (longId > int.MaxValue)
                return int.MaxValue;
            if (longId < int.MinValue)
                return int.MinValue;
            return ((int)longId);
        }

        //public void SaveData(string tableName)
        //{
        //    if (!IsConnected) return;
        //    int bufferSize = 1*1024*1024;
        //    byte[] buffer = new byte[bufferSize];
        //    var ret = ZKSdk.GetDeviceData(_connectionHandle, buffer, bufferSize, tableName, "*", "", "");
        //    if (ret.ToInt32() >= 0)
        //    {
        //        File.WriteAllText(tableName + ".txt", Encoding.Default.GetString(buffer));
        //    }
        //}

        private List<TblUser> _usersCache = new List<TblUser>();
        public List<TblUser> GetUsers(bool fromCache=true)
        {
            if (fromCache && _usersCache.Count > 0) return _usersCache;

            var users = new List<TblUser>();
            if (!IsConnected) return users;
           
            var tableName = "user";
            
            //var dataCount =  ZKSdk.GetDeviceDataCount(_connectionHandle, tableName, "", "");
            //if (dataCount == IntPtr.Zero)
            //{
            //    return users;
            //}
            //var dataSize = dataCount.ToInt32();
            int bufferSize = 128 * 1024; 
            byte[] buffer = new byte[bufferSize];

            var ret = ZKSdk.GetDeviceData(_connectionHandle, buffer, bufferSize, tableName, "UID\tCardNo\tPin\tPassword\tGroup\tName\tSuperAuthorize\tStartTime\tEndTime", "", "");
            if (ret.GetValue() >= 0)
            {
                var str = Encoding.Default.GetString(buffer);
                var lines = str.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var line in lines)
                {
                    var row = line.Split(',');
                    if (row.Length != 9) continue;
                    if (row[0] == "UID") continue;
                    var user = new TblUser();
                    user.UId = Convert.ToInt32(row[0]);
                    user.CardNo = Convert.ToInt32(row[1]);
                    user.Pin = Convert.ToInt32(row[2]);
                    user.Password = Convert.ToInt32(row[3]);
                    user.Group = Convert.ToInt32(row[4]);
                    user.Name = Convert.ToString(row[5]);
                    user.SuperAuthorize = Convert.ToInt32(row[6]);
                    user.StartTime = ZKSdk.ConvertDate( Convert.ToInt32(row[7]));
                    user.EndTime = ZKSdk.ConvertDate( Convert.ToInt32(row[8]));
                    users.Add(user);
                }
            }
            return _usersCache = users;
        }


        /// <summary>
        /// 给用户随机密码, 
        /// </summary>
        /// <param name="userId">用户编号</param>
        /// <returns>大于0表示执行成功,返回随机的密码; 负数表示执行失败</returns>
        public int SetRandomPassword(int userId)
        {
            var user = GetUser(userId);
            if (user == null) return -12;

            var pwd = _random.Next(100000,999999);
            user.Password = pwd;
            var ret = SetUserInfo(user);
            if (ret >= 0)
                return pwd;
            return -12;
        }
        public TblUser GetUser(int userId)
        {
            if (!IsConnected) return null;

            var tableName = "user";

            //var dataCount =  ZKSdk.GetDeviceDataCount(_connectionHandle, tableName, "", "");
            //if (dataCount == IntPtr.Zero)
            //{
            //    return users;
            //}
            //var dataSize = dataCount.ToInt32();
            int bufferSize = 1 * 1024;
            byte[] buffer = new byte[bufferSize];

            var ret = ZKSdk.GetDeviceData(_connectionHandle, buffer, bufferSize, tableName, "UID\tCardNo\tPin\tPassword\tGroup\tName\tSuperAuthorize\tStartTime\tEndTime", "Pin="+userId, "");
            if (ret.GetValue() >= 0)
            {
                var str = Encoding.Default.GetString(buffer);
                var lines = str.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var line in lines)
                {
                    var row = line.Split(',');
                    if (row.Length != 9) continue;
                    if (row[0] == "UID") continue;
                    var user = new TblUser();
                    user.UId = Convert.ToInt32(row[0]);
                    user.CardNo = Convert.ToInt32(row[1]);
                    user.Pin = Convert.ToInt32(row[2]);
                    user.Password = Convert.ToInt32(row[3]);
                    user.Group = Convert.ToInt32(row[4]);
                    user.Name = Convert.ToString(row[5]);
                    user.SuperAuthorize = Convert.ToInt32(row[6]);
                    user.StartTime = ZKSdk.ConvertDate(Convert.ToInt32(row[7]));
                    user.EndTime = ZKSdk.ConvertDate(Convert.ToInt32(row[8]));
                    return user;
                }
            }
            return null;
        }

        private List<TblTimezone> _timezonesCache = new List<TblTimezone>();
        public List<TblTimezone> GetTimezones(bool fromCache=true)
        {
            if (fromCache && _timezonesCache.Count > 0) return _timezonesCache;

            List<TblTimezone> timezones = new List<TblTimezone>();

            if (!IsConnected) return timezones;

            var tableName = "timezone";

            //var dataCount = ZKSdk.GetDeviceDataCount(_connectionHandle, tableName, "", "");
            //if (dataCount == IntPtr.Zero)
            //{
            //    return timezones;
            //}
            //var dataSize = dataCount.ToInt32();
            int bufferSize = 128 * 1024; //时间段不会很多,开辟128k缓冲
            byte[] buffer = new byte[bufferSize];

            var ret = ZKSdk.GetDeviceData(_connectionHandle, buffer, bufferSize, tableName, 
                "TimezoneId\tSunTime1\tSunTime2\tSunTime3\tMonTime1\tMonTime2\tMonTime3\tTueTime1\tTueTime2\tTueTime3\tWedTime1\tWedTime2\tWedTime3\tThuTime1\tThuTime2\tThuTime3\tFriTime1\tFriTime2\tFriTime3\tSatTime1\tSatTime2\tSatTime3\tHol1Time1\tHol1Time2\tHol1Time3\tHol2Time1\tHol2Time2\tHol2Time3\tHol3Time1\tHol3Time2\tHol3Time3", "", "");
            if (ret.GetValue() >= 0)
            {
                var str = Encoding.Default.GetString(buffer);
                var lines = str.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var line in lines)
                {
                    var row = line.Split(',');
                    if (row.Length != 31) continue;
                    if (row[0] == "TimezoneId") continue;
                    var timezone = new TblTimezone();
                    timezone.TimezoneId = Convert.ToInt32(row[0]);
                    timezone.SunTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[1] ));
                    timezone.SunTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[2] ));
                    timezone.SunTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[3] ));
                    timezone.MonTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[4] ));
                    timezone.MonTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[5] ));
                    timezone.MonTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[6] ));
                    timezone.TueTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[7] ));
                    timezone.TueTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[8] ));
                    timezone.TueTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[9] ));
                    timezone.WedTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[10]));
                    timezone.WedTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[11]));
                    timezone.WedTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[12]));
                    timezone.ThuTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[13]));
                    timezone.ThuTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[14]));
                    timezone.ThuTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[15]));
                    timezone.FriTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[16]));
                    timezone.FriTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[17]));
                    timezone.FriTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[18]));
                    timezone.SatTime1 = new TblTimezone.TimeRange( Convert.ToInt32(row[19]));
                    timezone.SatTime2 = new TblTimezone.TimeRange( Convert.ToInt32(row[20]));
                    timezone.SatTime3 = new TblTimezone.TimeRange( Convert.ToInt32(row[21]));
                    timezone.Hol1Time1 =new TblTimezone.TimeRange( Convert.ToInt32(row[22]));
                    timezone.Hol1Time2 =new TblTimezone.TimeRange( Convert.ToInt32(row[23]));
                    timezone.Hol1Time3 =new TblTimezone.TimeRange( Convert.ToInt32(row[24]));
                    timezone.Hol2Time1 =new TblTimezone.TimeRange( Convert.ToInt32(row[25]));
                    timezone.Hol2Time2 =new TblTimezone.TimeRange( Convert.ToInt32(row[26]));
                    timezone.Hol2Time3 =new TblTimezone.TimeRange( Convert.ToInt32(row[27]));
                    timezone.Hol3Time1 =new TblTimezone.TimeRange( Convert.ToInt32(row[28]));
                    timezone.Hol3Time2 =new TblTimezone.TimeRange( Convert.ToInt32(row[29]));
                    timezone.Hol3Time3 =new TblTimezone.TimeRange( Convert.ToInt32(row[30]));
                    timezones.Add(timezone);
                }
            }

            return _timezonesCache = timezones;
        }

        private List<TblUserAuthorize> _authorizeCache = new List<TblUserAuthorize>();
        public List<TblUserAuthorize> GetUserAuthorizes(bool fromCache = true)
        {
            if (fromCache && _authorizeCache.Count > 0) return _authorizeCache;

            List<TblUserAuthorize> authorizes = new List<TblUserAuthorize>();

            if (!IsConnected) return authorizes;

            var tableName = "userauthorize";

            //var dataCount = ZKSdk.GetDeviceDataCount(_connectionHandle, tableName, "", "");
            //if (dataCount == IntPtr.Zero)
            //{
            //    return authorizes;
            //}
            //var dataSize = dataCount.ToInt32();
            int bufferSize = 128 * 1024;//不会很多,开辟128k缓冲
            byte[] buffer = new byte[bufferSize];

            var ret = ZKSdk.GetDeviceData(_connectionHandle, buffer, bufferSize, tableName,
                "Pin\tAuthorizeTimezoneId\tAuthorizeDoorId", "", "");
            if (ret.GetValue() >= 0)
            {
                var str = Encoding.Default.GetString(buffer);
                var lines = str.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var line in lines)
                {
                    var row = line.Split(',');
                    if (row.Length != 3) continue;
                    if (row[0] == "Pin") continue;
                    var timezone = new TblUserAuthorize();
                    timezone.Pin = Convert.ToInt32(row[0]);
                    timezone.TimezoneId = Convert.ToInt32(row[1]);
                    timezone.DoorId = (AuthorizeDoor)Convert.ToInt32(row[2]);
                    authorizes.Add(timezone);
                }
            }
            return _authorizeCache = authorizes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="onlyNew"></param>
        /// <param name="userId">人员ID(pin)</param>
        /// <param name="begin">开始时间</param>
        /// <param name="end">结束时间</param>
        /// <returns></returns>
        public List<TblTransaction> GetLogs(bool onlyNew=false,int? userId=null, DateTime? begin = null, DateTime? end = null)
        {

            List<TblTransaction> logs = new List<TblTransaction>();

            if (!IsConnected) return logs;

            var tableName = "transaction";

            //var dataCount = ZKSdk.GetDeviceDataCount(_connectionHandle, tableName, "", "");
            //if (dataCount == IntPtr.Zero)
            //{
            //    return authorizes;
            //}
            //var dataSize = dataCount.ToInt32();
            int bufferSize = 1 * 1024 * 1024; //开辟1M的数据缓冲
            byte[] buffer = new byte[bufferSize];
            List<string> filters = new List<string>();
            if (userId.HasValue)
            {
                filters.Add("Pin>" + userId);
            }
            if (begin.HasValue)
            {//
                filters.Add("Time_second>" + ZKSdk.ConvertTimeInt(begin.Value));//ZKSdk.ConvertTimeInt(begin.Value)
            }
            if (end.HasValue)
            {
                filters.Add("Time_second<" + ZKSdk.ConvertTimeInt(end.Value));
            }
            var ret = ZKSdk.GetDeviceData(_connectionHandle, buffer, bufferSize, tableName,
                "Cardno\tPin\tVerified\tDoorID\tEventType\tInOutState\tTime_second", String.Join(",", filters), onlyNew? "NewRecord":"");
            if (ret.GetValue() >= 0)
            {
                var str = Encoding.Default.GetString(buffer);
                var lines = str.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (var line in lines)
                {
                    var row = line.Split(',');
                    if (row.Length != 7) continue;
                    if (row[0] == "Cardno") continue;
                    var timezone = new TblTransaction();
                    timezone.Cardno = Convert.ToInt32(row[0]);
                    timezone.Pin = Convert.ToInt32(row[1]);
                    timezone.Verified = Convert.ToInt16(row[2]);
                    timezone.DoorID = Convert.ToInt32(row[3]);
                    timezone.EventType = Convert.ToInt16(row[4]);
                    timezone.InOutState = Convert.ToInt16(row[5]);
                    timezone.Time_second = ZKSdk.ConvertDatetime( Convert.ToInt32(row[6]));
                    logs.Add(timezone);
                }
            }
            return logs;
        }
        /// <summary>
        /// 获取实时状态
        /// </summary>
        /// <returns></returns>
        public DeviceStatus GetRealStatus()
        {
            var status = new DeviceStatus();
            status.Status = -1;
            if (!IsConnected) return status;

            int bufferSize = 256 * 1024;
            byte[] buffer = new byte[bufferSize];

            var ret = ZKSdk.GetRTLog(_connectionHandle, buffer, bufferSize);
            if (ret.GetValue() >= 0)
            {
                var str = Encoding.Default.GetString(buffer).Replace("\0", "");
                var lines = str.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                if(lines.Length<1) return status;
                var line0 = lines[0].Split(',');
                if (line0[4] == "255")
                {
                    var statusPart = line0;
                    if (statusPart.Length != 7) return status;
                    status.Time = Convert.ToDateTime(statusPart[0]);
                    status.Status = Convert.ToInt16(statusPart[1]);
                    status.AlarmStatus = Convert.ToInt16(statusPart[2]);
                }
                else
                {
                    for (int i = 0; i < lines.Length; i++)
                    {
                        var newRecordPart = lines[i].Split(',');
                        if (newRecordPart.Length != 7) continue;
                        var record = new TblTransaction();
                        record.Time_second = Convert.ToDateTime(newRecordPart[0]);
                        record.Pin = Convert.ToInt32(newRecordPart[1]);
                        record.Cardno = Convert.ToInt32(newRecordPart[2]);
                        record.EventType = Convert.ToInt16(newRecordPart[4]);
                        record.InOutState = Convert.ToInt32(newRecordPart[5]);
                        record.Verified = Convert.ToInt32(newRecordPart[6]);
                        status.NewRecords.Add(record);
                    }
                    if (status.NewRecords.Count > 0)
                    {
                        var last = status.NewRecords.Last();
                        status.Time = last.Time_second;
                        status.Status = (short) (last.InOutState == 0 ? 2: 1);
                    }
                }
            }
            return status;
        }


        public void Dispose()
        {
            DisConnect();
        }
    }
}